import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
import * as moment from 'moment';
import * as path from 'path';
import { randomBytes } from 'crypto';
import Decimal from 'decimal.js'

/**
 * 帮助类
 */
@Provide()
@Scope(ScopeEnum.Singleton)
export class Utils {
  @Inject()
  baseDir;

  /**
   * 获得dist路径
   */
  getDistPath() {
    const runPath = __dirname;
    const distIndex =
      runPath.lastIndexOf('/dist/') !== -1
        ? runPath.lastIndexOf('/dist/')
        : runPath.lastIndexOf('\\dist\\');
    if (distIndex !== -1) {
      return path.join(runPath.substring(0, distIndex), 'dist');
    }
    return path.join(runPath, 'dist');
  }

  /**
   * 获得请求IP
   */
  async getReqIP(ctx: Context) {
    const req = ctx.req;
    return (
      req.headers['x-forwarded-for'] ||
      req.socket.remoteAddress?.replace('::ffff:', '') ||
      ''
    );
  }

  /**
   * 去除对象的空值属性
   * @param obj
   */
  async removeEmptyP(obj) {
    Object.keys(obj).forEach(key => {
      if (obj[key] === null || obj[key] === '' || obj[key] === 'undefined') {
        delete obj[key];
      }
    });
  }

  /**
   * 线程阻塞毫秒数
   * @param ms
   */
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * 获得最近几天的日期集合
   * @param recently
   */
  getRecentlyDates(recently, format = 'YYYY-MM-DD') {
    moment.locale('zh-cn');
    const dates = [];
    for (let i = 0; i < recently; i++) {
      dates.push(moment().subtract(i, 'days').format(format));
    }
    return dates.reverse();
  }
  /**
   * 获得最近几个月的月数
   * @param recently
   */
  getRecentlyMonths(recently, format = 'YYYY-MM') {
    moment.locale('zh-cn');
    const dates = [];
    const date = moment(Date.now()).format('YYYY-MM');
    for (let i = 0; i < recently; i++) {
      dates.push(moment(date).subtract(i, 'months').format(format));
    }
    return dates.reverse();
  }

  /**
   * 根据开始和结束时间，获得时间段内的日期集合
   * @param start
   * @param end
   */
  getBetweenDays(start, end, format = 'YYYY-MM-DD') {
    moment.locale('zh-cn');
    const dates = [];
    const startTime = moment(start).format(format);
    const endTime = moment(end).format(format);
    const days = moment(endTime).diff(moment(startTime), 'days');
    for (let i = 0; i <= days; i++) {
      dates.push(moment(startTime).add(i, 'days').format(format));
    }
    return dates;
  }

  /**
   * 根据开始和结束时间，获得时间段内的月份集合
   * @param start
   * @param end
   */
  getBetweenMonths(start, end, format = 'YYYY-MM') {
    moment.locale('zh-cn');
    const dates = [];
    const startTime = moment(start).format(format);
    const endTime = moment(end).format(format);
    const months = moment(endTime).diff(moment(startTime), 'months');
    for (let i = 0; i <= months; i++) {
      dates.push(moment(startTime).add(i, 'months').format(format));
    }
    return dates;
  }

  /**
   * 根据开始和结束时间，获得时间段内的小时集合
   * @param start
   * @param end
   */
  getBetweenHours(start, end, format = 'YYYY-MM-DD HH') {
    moment.locale('zh-cn');
    const dates = [];
    const startTime = moment(start).format(format);
    const endTime = moment(end).format(format);
    const hours = moment(endTime).diff(moment(startTime), 'hours');
    for (let i = 0; i <= hours; i++) {
      dates.push(moment(startTime).add(i, 'hours').format(format));
    }
    return dates;
  }

  /**
   * 字段转驼峰法
   * @param obj
   * @returns
   */
  toCamelCase(obj) {
    const camelCaseObject = {};
    for (const i in obj) {
      const camelCase = i.replace(/([-_][a-z])/gi, $1 => {
        return $1.toUpperCase().replace('-', '').replace('_', '');
      });
      camelCaseObject[camelCase] = obj[i];
    }
    return camelCaseObject;
  }

  /**
   * 匹配URL
   * @param pattern
   * @param url
   * @returns
   */
  matchUrl(pattern: string, url: string) {
    // 将 pattern 和 url 按 `/` 分割
    const patternParts = pattern.split('/').filter(Boolean);
    const urlParts = url.split('/').filter(Boolean);
    // 如果长度不匹配且 pattern 不包含 **，直接返回 false
    if (patternParts.length !== urlParts.length && !pattern.includes('**')) {
      return false;
    }
    for (let i = 0; i < patternParts.length; i++) {
      const patternPart = patternParts[i];
      const urlPart = urlParts[i];
      // 如果 patternPart 是 **，匹配剩余的所有部分
      if (patternPart === '**') {
        return true;
      }
      // 如果 patternPart 以 : 开头，说明是参数，直接匹配任意非空值
      if (patternPart.startsWith(':')) {
        if (!urlPart) {
          return false;
        }
        continue;
      }
      // 如果 patternPart 是 *，匹配任意非空部分
      if (patternPart === '*') {
        if (!urlPart) {
          return false;
        }
      } else if (patternPart !== urlPart) {
        return false;
      }
    }
    // 如果 pattern 和 url 的部分数量一致，则匹配成功
    return patternParts.length === urlParts.length;
  }

  /**
   * 从文本中提取 JSON 字符串并转换为对象
   * @param {string} text - 可能包含 JSON 的文本
   * @returns {Object|Array|null} - 解析出的 JSON 对象，如果没有找到有效 JSON 则返回 null
   */
  extractJSONFromText(text: string) {
    if (!text || typeof text !== 'string') {
      return null;
    }

    try {
      // 尝试直接解析整个文本
      return JSON.parse(text);
    } catch (e) {
      // 整个文本不是有效的 JSON，尝试提取 JSON 部分
    }

    // 查找可能的 JSON 开始位置（{ 或 [）
    const possibleStarts = [];
    for (let i = 0; i < text.length; i++) {
      if (text[i] === '{' || text[i] === '[') {
        possibleStarts.push(i);
      }
    }

    // 从每个可能的起始位置尝试提取 JSON
    for (const startIndex of possibleStarts) {
      let openBraces = 0;
      let openBrackets = 0;
      let inString = false;
      let escapeNext = false;

      for (let i = startIndex; i < text.length; i++) {
        const char = text[i];

        // 处理转义字符
        if (inString && !escapeNext && char === '\\') {
          escapeNext = true;
          continue;
        }

        // 处理字符串边界
        if (!escapeNext && char === '"') {
          inString = !inString;
        }

        if (!inString) {
          // 只在不在字符串内部时才计算括号
          if (char === '{') openBraces++;
          else if (char === '}') openBraces--;
          else if (char === '[') openBrackets++;
          else if (char === ']') openBrackets--;
        }

        escapeNext = false;

        // 检查是否找到了完整的 JSON 结构
        if (
          (openBraces === 0 && text[startIndex] === '{' && char === '}') ||
          (openBrackets === 0 && text[startIndex] === '[' && char === ']')
        ) {
          const jsonStr = text.substring(startIndex, i + 1);
          try {
            const result = JSON.parse(jsonStr);
            return result;
          } catch (e) {
            // 这个候选 JSON 无效，继续尝试下一个
            break;
          }
        }
      }
    }

    return null; // 没有找到有效的 JSON
  }

  /**
   * 判断两个时间段是否重叠
   * @param aStart 第一个时间段开始时间
   * @param aEnd   第一个时间段结束时间
   * @param bStart 第二个时间段开始时间
   * @param bEnd   第二个时间段结束时间
   * @param inclusive 边界是否算重叠，默认 true（闭区间）
   * @returns 是否重叠
   */
  isTimeRangeOverlap(aStart: Date, aEnd: Date, bStart: Date, bEnd: Date, inclusive = false) {
    // 参数校验
    if (!aStart || !aEnd || !bStart || !bEnd) return false;

    const aS = moment(aStart);
    const aE = moment(aEnd);
    const bS = moment(bStart);
    const bE = moment(bEnd);

    if (!aS.isValid() || !aE.isValid() || !bS.isValid() || !bE.isValid()) {
      return false;
    }

    // 规范化起止顺序
    const aMin = moment.min(aS, aE);
    const aMax = moment.max(aS, aE);
    const bMin = moment.min(bS, bE);
    const bMax = moment.max(bS, bE);

    if (inclusive) {
      // 闭区间重叠：aStart <= bEnd && bStart <= aEnd
      return aMin.valueOf() <= bMax.valueOf() && bMin.valueOf() <= aMax.valueOf();
    }
    // 开区间重叠：aStart < bEnd && bStart < aEnd（边界相等不算重叠）
    return aMin.valueOf() < bMax.valueOf() && bMin.valueOf() < aMax.valueOf();
  }

  /**
   * 生成唯一ID：前缀_YYYYMMDDHHmmssSSS+随机3位数字
   * @param prefix 自定义前缀，如：PRODUCT
   * @param date 可选，指定时间，不传则使用当前时间
   */
  generateUniqueId(prefix: string, date: Date = new Date()) {
    const timeStr = moment(date).format('YYYYMMDDHHmmssSSS');
    const randStr = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
    return `${prefix}_${timeStr}${randStr}`;
  }

  /**
   * 生成 UUID v4
   */
  uuid() {
    const b = randomBytes(16);
    // 设置版本与变体位：v4 与 RFC 4122 variant
    b[6] = (b[6] & 0x0f) | 0x40;
    b[8] = (b[8] & 0x3f) | 0x80;

    const hex: string[] = [];
    for (let i = 0; i < b.length; i++) {
      hex.push((b[i] + 0x100).toString(16).substring(1));
    }
    return `${hex[0]}${hex[1]}${hex[2]}${hex[3]}-${hex[4]}${hex[5]}-${hex[6]}${hex[7]}-${hex[8]}${hex[9]}-${hex[10]}${hex[11]}${hex[12]}${hex[13]}${hex[14]}${hex[15]}`;
  }

  /**
   * 将数字转换为base62编码
   * @param num 要转换的数字
   * @param length 编码后的长度，不足则前面补0
   * @returns base62编码字符串
   */
  private numberToBase62(num: number, length?: number): string {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    let n = num;
    
    if (n === 0) {
      result = '0';
    } else {
      while (n > 0) {
        result = chars[n % 62] + result;
        n = Math.floor(n / 62);
      }
    }
    
    // 如果指定了长度，不足则前面补0
    if (length && result.length < length) {
      result = '0'.repeat(length - result.length) + result;
    }
    
    return result;
  }

  /**
   * 生成6位唯一字符（包含大小写字母和数字）
   * 结合时间戳和随机字符，确保唯一性
   * @param checkExists 可选，检查函数，返回 true 表示已存在，需要重新生成
   * @param maxRetries 最大重试次数，默认10次
   * @returns 6位随机字符串
   */
  async generateUniqueCode(checkExists?: (code: string) => Promise<boolean> | boolean, maxRetries: number = 10): Promise<string> {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    
    for (let attempt = 0; attempt < maxRetries; attempt++) {
      // 获取当前时间戳（毫秒）
      const timestamp = Date.now();
      // 将时间戳转换为base62编码，取后3位（确保时间唯一性）
      const timeBase62 = this.numberToBase62(timestamp);
      // 确保时间部分至少3位，不足则用第一个字符补齐
      const timePart = timeBase62.length >= 3 
        ? timeBase62.slice(-3) 
        : (timeBase62 + chars[0]).slice(-3);
      
      // 生成3位随机字符
      let randomPart = '';
      const bytes = randomBytes(3);
      for (let i = 0; i < 3; i++) {
        randomPart += chars[bytes[i] % chars.length];
      }
      
      // 组合：时间戳部分（3位）+ 随机部分（3位）= 6位
      const result = timePart + randomPart;
      
      // 如果提供了检查函数，则验证唯一性
      if (checkExists) {
        const exists = await checkExists(result);
        if (!exists) {
          return result;
        }
        // 如果已存在，继续下一次尝试（等待下一毫秒或使用随机字符）
      } else {
        // 如果没有检查函数，直接返回（基于时间戳，唯一性更高）
        return result;
      }
    }
    
    // 如果达到最大重试次数仍未生成唯一码，抛出错误
    throw new Error(`生成唯一码失败，已重试 ${maxRetries} 次`);
  }

  /**
   * 高精度加法
   * @param a 第一个数
   * @param b 第二个数
   * @returns 
   */
  add(a: number, b: number) {
    return new Decimal(a).add(b).toNumber();
  }
  /**
   * 高精度减法
   * @param a 第一个数
   * @param b 第二个数
   * @returns 
   */
  sub(a: number, b: number) {
    return new Decimal(a).sub(b).toNumber();
  }
  /**
   * 高精度乘法
   * @param a 第一个数
   * @param b 第二个数
   * @returns 
   */
  mul(a: number, b: number) {
    return new Decimal(a).mul(b).toNumber();
  }
  /**
   * 高精度除法
   * @param a 第一个数
   * @param b 第二个数
   * @returns 
   */
  div(a: number, b: number) {
    return new Decimal(a).div(b).toNumber();
  }
}
